import FLV from "flv.js";

interface Config {
    autoForward: boolean
}

export class FLVPlayer {
    static defaultConfig: FLV.Config = {
        autoCleanupSourceBuffer: true,
        stashInitialSize: 1024,
    }

    readonly flv: FLV.Player;
    readonly mediaElement: HTMLMediaElement;

    isAutoForwardEnabled: boolean = false //启用此选项会在播放进度落后时自动追赶，一般用于直播视频。

    private timer: number;
    private isPausedByUser = false;
    private loadingComplete = false;

    private onBufferedUpdate?: (n: number) => void
    private get bufferedDuration() {
        try {
            if (!this.flv.buffered.length) return 0;
            return this.flv.buffered.end(this.flv.buffered.length - 1) - this.flv.currentTime;
        }catch (error) {
            return 0
        }
    }

    constructor(mediaElement: HTMLMediaElement, source: FLV.MediaDataSource, config?: FLV.Config & Config) {
        this.mediaElement = mediaElement
        const defaultConfigCopy = Object.assign({},FLVPlayer.defaultConfig);
        const flv = FLV.createPlayer(source, Object.assign(defaultConfigCopy,config));
        console.log(`[FLVPlayer] begin attach media element`);
        flv.attachMediaElement(mediaElement);
        console.log(`[FLVPlayer] end attach media element`);
        flv.on(FLV.Events.ERROR, this.onError.bind(this));
        flv.on(FLV.Events.LOADING_COMPLETE, () => {
            console.log('LOADING_COMPLETE');
            this.loadingComplete = true;
            if (this.paused && !this.isPausedByUser) {
                this.flv.play();
            }
        })
        this.flv = flv;
        this.isAutoForwardEnabled = config && config.autoForward || false;
        this.timer = window.setInterval(()=> {
            try {
                if (!this.flv.buffered.length) return;
            }catch (error) {
                clearInterval(this.timer);
                return;
            }
            const offset = this.bufferedDuration
            console.log('缓冲长度',offset);
            this.onBufferedUpdate && this.onBufferedUpdate(offset);
            if (this.isPausedByUser || this.loadingComplete) return;
            if (this.mediaElement.paused) {
                if (offset > 2) {
                    this.flv.play();
                }
            }else {
                if (offset < 0.5) {
                    this.flv.pause();
                    this.mediaElement.dispatchEvent(new Event('waiting'));
                }else if (this.isAutoForwardEnabled && offset > 12) {
                    this.flv.currentTime = this.flv.currentTime + offset - 3;
                }
            }
        },1000);
        this.mediaElement.onended = () => clearInterval(this.timer);
    }

    async load(): Promise<void> {
        console.log(`[FLVPlayer] begin load`);
        this.flv.load();
        console.log(`[FLVPlayer] end load`);
        return;
    }

    private onError(errorType: string, errorDetail: string, errorInfo: any) {
        this.flv.destroy()
        switch (errorDetail) {
            case FLV.ErrorDetails.NETWORK_UNRECOVERABLE_EARLY_EOF:
                if (this.bufferedDuration <= 2) {
                    this.sendError(new Error(`视频流已断开: ${errorInfo.code} ${errorInfo.msg}`))
                } else {
                    this.onBufferedUpdate = (buffered) => {
                        if (buffered <= 2) {
                            this.sendError(new Error(`视频流已断开: ${errorInfo.code} ${errorInfo.msg}`))
                            delete this.onBufferedUpdate;
                        }
                    }
                }
                break;
            case FLV.ErrorDetails.NETWORK_STATUS_CODE_INVALID:
                this.sendError(new Error(`HTTP状态码错误: ${errorInfo.code} ${errorInfo.msg}`))
                break;
            case FLV.ErrorDetails.NETWORK_TIMEOUT:
                this.sendError(new Error(`请求超时: ${errorInfo.code} ${errorInfo.msg}`))
                break;
            case FLV.ErrorDetails.NETWORK_EXCEPTION:
                this.sendError(new Error(`网络错误: ${errorInfo.code} ${errorInfo.msg}`))
                break;
            case FLV.ErrorDetails.MEDIA_CODEC_UNSUPPORTED:
                this.sendError(new Error(`媒体流包含不支持的视频/音频编解码器: ${errorInfo.code} ${errorInfo.msg}`))
                break;
            case FLV.ErrorDetails.MEDIA_FORMAT_UNSUPPORTED:
                this.sendError(new Error(`媒体格式不支持: ${errorInfo.code} ${errorInfo.msg}`))
                break;
            case FLV.ErrorDetails.MEDIA_FORMAT_ERROR:
                this.sendError(new Error(`媒体流格式错误: ${errorInfo.code} ${errorInfo.msg}`))
                break;
            case FLV.ErrorDetails.MEDIA_MSE_ERROR:
                this.sendError(new Error(JSON.stringify(errorInfo)))
                break;
        }
    }

    sendError(error:Error) {
        this.mediaElement.dispatchEvent(new ErrorEvent('error', {
            error: error,
            message: error.message
        }))
    }

    destroy() {
        console.log(`[FLVPlayer] begin destroy`);
        window.clearInterval(this.timer)
        try {
            if (this.flv.buffered) {
                this.pause();
                this.flv.destroy();
            }
        }catch (error) {
            console.log(`[FLVPlayer] already destroy`);
        }
        console.log(`[FLVPlayer] end destroy`);
    }

    get buffered(): TimeRanges {
        return this.mediaElement.buffered;
    }
    get currentTime(): number {
        return this.flv.currentTime;
    }
    set currentTime(value) {
        this.flv.currentTime = value;
    }

    get defaultPlaybackRate(): number {
        return this.mediaElement.defaultPlaybackRate;
    }

    set defaultPlaybackRate(value) {
        this.mediaElement.defaultPlaybackRate = value;
    }

    get duration(): number {
        return this.mediaElement.duration;
    }
    get muted(): boolean {
        return this.mediaElement.muted;
    }
    set muted(value) {
        this.mediaElement.muted = value;
    }

    get paused(): boolean {
        return this.mediaElement.paused
    }

    get playbackRate(): number {
        return this.mediaElement.playbackRate;
    }
    set playbackRate(value) {
        this.mediaElement.playbackRate = value;
    }

    get played(): TimeRanges {
        return this.mediaElement.played;
    }

    get seekable(): TimeRanges {
        return this.mediaElement.seekable;
    }

    get volume(): number {
        return this.mediaElement.volume;
    }
    set volume(value) {
        this.mediaElement.volume = value;
    }

    pause() {
        this.flv.pause();
        this.isPausedByUser = true;
    }

    play() {
        const promise = this.flv.play();
        this.isPausedByUser = false;
        if (promise) {
            return promise;
        }else {
            return Promise.resolve();
        }
    }
}